Understanding the complete lifecycle of a Phoenix LiveView from mount to termination
A LiveView is a process that receives events, updates its state, and renders updates to a page as diffs. Understanding the lifecycle is essential for building robust real-time applications.
A LiveView begins as a regular HTTP request and HTML response, then upgrades to a stateful view on client connect. This two-phase lifecycle guarantees a regular HTML page even if JavaScript is disabled.
1
Initial HTTP Request
The LiveView is rendered statically as part of a normal HTTP response
2
WebSocket Connection
The client establishes a WebSocket connection to the server
3
Stateful Process
A LiveView process is spawned and mounted on the server
4
Event Handling
The process handles events and pushes diffs to the client
When a user first accesses a LiveView, it’s rendered in its disconnected state as part of a regular HTML response.
def mount(_params, _session, socket) do # This runs during BOTH disconnected and connected mounts if connected?(socket) do # Only runs when WebSocket is connected :timer.send_interval(1000, self(), :tick) end {:ok, assign(socket, :date, :calendar.local_time())}end
Use connected?/1 to detect whether the mount is happening during the initial HTTP request (disconnected) or after the WebSocket connection (connected).
The mount/3 callback is invoked when the LiveView starts. It’s called twice for each LiveView: once during disconnected render and once when the WebSocket connects.
def mount(_params, _session, socket) do {:ok, assign(socket, :count, 0)}end
def mount(%{"id" => id}, _session, socket) do user = Accounts.get_user!(id) {:ok, assign(socket, :user, user)}end
def mount(_params, %{"user_id" => user_id}, socket) do user = Accounts.get_user!(user_id) {:ok, assign(socket, :current_user, user)}end
def mount(_params, _session, socket) do if connected?(socket) do Phoenix.PubSub.subscribe(MyApp.PubSub, "updates") end {:ok, assign(socket, :messages, [])}end
The handle_params/3 callback is invoked after mount and whenever there is a live navigation event (like push_patch/2 or <.link patch={...}>). This is only available for LiveViews mounted at the router.
Define reusable mount logic that runs before your LiveView’s mount callback:
defmodule MyAppWeb.UserAuth do import Phoenix.LiveView def on_mount(:require_authenticated, _params, session, socket) do case session do %{"user_id" => user_id} -> {:cont, assign(socket, :current_user, get_user!(user_id))} _ -> {:halt, redirect(socket, to: "/login")} end endend
Then use it in your router:
live_session :authenticated, on_mount: {MyAppWeb.UserAuth, :require_authenticated} do live "/dashboard", DashboardLiveend
def mount(_params, _session, socket) do if connected?(socket) do # Resubscribe on reconnection Phoenix.PubSub.subscribe(MyApp.PubSub, "topic") end {:ok, socket}end